;; db.lisp - Implementación de una base de datos simple
;;
;;  Autores:
;;    Federico Churca Torrusio <fchurca@fi.uba.ar>
;;      FIUBA - Universidad de Buenos Aires, Facultad de Ingeniería
;;      Buenos Aires, Argentina
;;    Federico Farina <federicofarina22@gmail.com>
;;      FIUBA - Universidad de Buenos Aires, Facultad de Ingeniería
;;      Buenos Aires, Argentina

;;;; Solución funcional

;;; Declaro una variable global donde voy a ir guardando el contenido que vaya cargando.

(defvar  *db* nil)

;;; Guardo los datos cargados en un archivo 

(defun save-db (filename)
  (with-open-file (out filename :direction :output :if-exists :supersede)
    (with-standard-io-syntax
    (print *db* out)))) 

;;; Cargo los datos desde un archivo 

(defun load-db (filename)
  (with-open-file (in filename)
    (with-standard-io-syntax
      (setf *db* (read in)))))

;;; Creo un CD nuevo (será el contenido de mis registros) 

(defun make-cd (title artist rating)
  (list :title title :artist artist :rating rating))

;;; Agrego un registro a la bse de datos 

(defun add-record (cd)
  (push cd *db*))

;;; Soporta diferentes funciones como argumentos , la función REMOVE-IF-NOT toma un predicado y una lista
;;; y devuelve una lista conteniendo solo los elementos de la primer lista que coincidan con el predicado.

(defun select (selector-fn)
  (remove-if-not selector-fn *db*))

;;; Toma 3 parámetros keywords correspondientes a los campos en los CDs y genera un selector-fn (función selectora)
;;; que selecciona cualquier CD que matchee con todos los valores pasados a where.

(defun where (&key title artist rating)
  (lambda (cd)
    (and
      (if title (string-equal (getf cd :title) title) t)
      (if artist (string-equal (getf cd :artist) artist) t)
      (if rating (eql (getf cd :rating) rating) t))))

